package org.spruice;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.MutablePropertyValues;
import org.spruice.proxy.BeanCallback;
import org.spruice.proxy.ProxyCreator;

import java.lang.reflect.Method;
import java.util.*;

public class InstanceConfig<T> implements LifecycleConfig<T> {
    private String name;
    private T instance;
    private ProxyCreator proxy;
    private List<Runnable> startables;
    private List<Runnable> stoppables;
    private List<Runnable> destroyables;

    public InstanceConfig(String name, T instance, ProxyCreator proxy,
                          List<Runnable> startables,
                          List<Runnable> stoppables,
                          List<Runnable> destroyables) {
        this.name = name;
        this.instance = instance;
        this.proxy = proxy;
        this.startables = startables;
        this.stoppables = stoppables;
        this.destroyables = destroyables;
    }

    public InstanceConfig<T> config(Properties props) {
        Map<String, String> instanceProps = filterByName(props, getName());
        BeanWrapperImpl wrapper = new BeanWrapperImpl(instance);
        wrapper.setPropertyValues(new MutablePropertyValues(instanceProps), false, false);
        return this;
    }

    static Map<String, String> filterByName(Properties props, String name) {
        Set<String> keys = props.stringPropertyNames();
        Map<String, String> result = new HashMap<String, String>();
        String objKey = name + ".";
        for (String key : keys) {
            if (key.startsWith(objKey)) {
                result.put(key.substring(objKey.length()), props.getProperty(key));
            }
        }
        return result;
    }

    public String getName() {
        return name;
    }

    public T startWith() {
        return proxy(startables);
    }

    public T stopWith() {
        return proxy(stoppables);
    }

    public T destroyWith() {
        return proxy(destroyables);
    }

    private T proxy(final List<Runnable> targetList) {
        MethodInterceptor advice = new MethodInterceptor() {
            public Object intercept(Object obj, final Method method, final Object[] args, MethodProxy proxy) throws Throwable {
                Runnable start = new Runnable() {
                    public void run() {
                        BeanCallback.invoke(instance, method, args);
                    }
                };
                targetList.add(start);
                return null;
            }
        };
        return createProxy(advice);
    }

    T createProxy(final MethodInterceptor advice) {
        return (T) proxy.imposterise(new BeanCallback(name, advice), instance.getClass());
    }

}
